﻿using Client.Framework;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEditor;
using UnityEngine;

public class GenerateScript
{
    public Dictionary<string, UIInfo> infoDic = new Dictionary<string, UIInfo>();

    private Dictionary<string, Type> typeMap = new Dictionary<string, Type>()
    {
        {"obj", typeof(GameObject)},
    };

    protected Transform transform;

    public SerializeConfig serializeCfg;

    readonly string tagIgnore = "Ignore";
    public string ClassName { get; set; }
    public bool Script { get; private set; }

    public string scriptName = string.Empty;

    public void InitData(GameObject go, bool script)
    {
        serializeCfg = SerializeConfig.Singleton;
        scriptName = serializeCfg.GetTemplateFile(script, true);
        foreach (var item in serializeCfg.typeMap)
        {
            typeMap[item.Value] = item.Key;
        }

        string fileName = GetFirstUpperStr(go.name);
        ClassName = fileName + serializeCfg.ClassSuffix;
        Script = script; transform = go.transform;
        infoDic.Clear(); ReadChild(transform);
    }

    public bool IsValidName(string objName)
    {
        return UITools.IsValidName(objName);
    }

    public string GetFirstUpperStr(string text)
    {
        if (!string.IsNullOrEmpty(text))
        {
            if (text.Length > 1)
            {
                return char.ToUpper(text[0]) + text.Substring(1);
            }
            return char.ToUpper(text[0]).ToString();
        }
        return string.Empty;
    }

    public void ReadChild(Transform tf)
    {
        foreach (Transform child in tf)
        {
            string path = child.BuildTfPath(transform);
            if (child.tag != tagIgnore)
            {
                string[] typeArr = child.name.Split('_');
                if (typeArr.Length >= 2)
                {
                    var typeName = typeArr[typeArr.Length - 1];
                    if (string.IsNullOrEmpty(typeName)) { continue; }

                    string field = child.name.Replace(typeName, "");
                    if (!IsValidName(field))
                    {
                        Debug.LogWarning("Field Name Is Not Invalid =" + child.name);
                        continue;
                    }

                    string[] typeKeys = typeName.Split('|');
                    for (int i = 0; i < typeKeys.Length; i++)
                    {
                        string typeKey = typeKeys[i];
                        if (typeMap.ContainsKey(typeKey))
                        {
                            AddUIInfo(field + typeKey, typeMap[typeKey], path);
                        }
                    }
                }
                if (child.childCount > 0) { ReadChild(child); }
            }
            else
            {
                if (!IsValidName(child.name))
                {
                    Debug.LogWarning("Field Name Is Not Invalid =" + child.name);
                }
                else { AddUIInfo(child.name + "_obj", typeof(GameObject), path); }
            }
        }
    }

    public void AddUIInfo(string name, Type type, string path)
    {
        var info = new UIInfo(name);
        infoDic[info.Name] = info;

        if (Script)
        {
            info.InitCSInfo(type, path);
        }
        else { info.InitLuaInfo(type, path); }
    }

    public void SaveScript(string classPath, string template)
    {
        if (IsValidName(ClassName))
        {
            var writer = new StreamWriter(classPath, false, new UTF8Encoding());
            writer.Write(GenerateString(template)); writer.Close();
        }
        else { Debug.LogWarning("Invalid class name == " + ClassName); }
    }

    public string GenerateString(string tempPath)
    {
        StreamReader streamReader = new StreamReader(tempPath);
        var template = streamReader.ReadToEnd(); streamReader.Close();
        template = template.Replace("@ClassName", ClassName);

        var fields = new StringBuilder();
        var body = new StringBuilder();
        for (int i = 0; i < infoDic.Count; i++)
        {
            UIInfo info = infoDic.GetValueByIdx(i, null); ;
            if (info.IsSelected)
            {
                body.Append(info.Body);
                fields.Append(info.Field);
            }
        }

        template = template.Replace("@body", body.ToString());
        return template.Replace("@fields", fields.ToString());
    }
}

public class CreateClassWindow : EditorWindow, ICreateWindow
{
    private Vector2 previewScrollPos;
    private Vector2 eventsScrollPos;
    static GenerateScript generate = new GenerateScript();

    #region methods

    public void CreateWindow(GameObject go, bool script)
    {
        generate.InitData(go, script);
        var dialog = GetWindow<CreateClassWindow>(true, "Create Script - " + go.name);
        dialog.minSize = new Vector2(960, 720);
    }

    #endregion

    private void OnGUI()
    {
        EditorGUILayout.BeginHorizontal();
        {
            GUILayout.Space(10);
            ShowPreview();
            GUILayout.Space(10);

            EditorGUILayout.BeginVertical();
            {
                ShowOptions();
                GUILayout.Space(10);
                ShowButtons();
            }
            EditorGUILayout.EndVertical();
            GUILayout.Space(10);
        }
        EditorGUILayout.EndHorizontal();
        GUILayout.Space(10);
    }

    private void ShowPreview()
    {
        float width = Mathf.Max(position.width * 0.4f, position.width - 380f);
        EditorGUILayout.BeginVertical(GUILayout.Width(width));
        {
            Rect previewHeaderRect = GUILayoutUtility.GetRect(new GUIContent("Preview"), "OL Title");
            previewScrollPos = EditorGUILayout.BeginScrollView(previewScrollPos, "OL Box");
            {
                EditorGUILayout.BeginHorizontal();
                {
                    string tempPath = generate.serializeCfg.tempPath + generate.scriptName;
                    string previewStr = generate.GenerateString(tempPath); GUILayout.Space(5);

                    Rect rect = GUILayoutUtility.GetRect(new GUIContent(previewStr), EditorStyles.miniLabel,
                        GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(true));
                    EditorGUI.TextArea(rect, previewStr, EditorStyles.miniLabel);
                }
                EditorGUILayout.EndHorizontal();
            }
            EditorGUILayout.EndScrollView();
            GUI.Label(previewHeaderRect, new GUIContent("Preview"), "OL Title");
            GUILayout.Space(4);
        }
        EditorGUILayout.EndVertical();
    }

    private void ShowButtons()
    {
        GUILayout.BeginHorizontal();
        {
            GUILayout.FlexibleSpace();
            if (GUILayout.Button("Cancel", GUILayout.Width(120)))
            {
                Close(); GUIUtility.ExitGUI();
            }

            bool guiEnabledTemp = GUI.enabled;
            if (GUILayout.Button("Create", GUILayout.Width(120)))
            {
                SaveAndAttachScript();
            }
            GUI.enabled = guiEnabledTemp;
        }
        GUILayout.EndHorizontal();
    }

    private void ShowOptions()
    {
        generate.ClassName = EditorGUILayout.TextField("Class Name", generate.ClassName);
        EditorGUILayout.BeginVertical("TextField", GUILayout.ExpandWidth(true));
        {
            eventsScrollPos = EditorGUILayout.BeginScrollView(eventsScrollPos);
            {
                for (int i = 0; i < generate.infoDic.Count; i++)
                {
                    Rect toggleRect = GUILayoutUtility.GetRect(GUIContent.none, EditorStyles.toggle);
                    toggleRect.x += 15; toggleRect.width -= 15;
                    var evt = generate.infoDic.GetValueByIdx(i, null);
                    evt.IsSelected = GUI.Toggle(toggleRect, evt.IsSelected, evt.Name);
                }
            }
            EditorGUILayout.EndScrollView();
        }
        EditorGUILayout.EndVertical();
    }

    private void SaveAndAttachScript()
    {
        string fullPath = generate.serializeCfg.GetScriptPath(generate.Script, out string scriptExt);
        fullPath = Application.dataPath + fullPath;
        fullPath = EditorUtility.SaveFilePanel("Create Script", fullPath, generate.ClassName, scriptExt);
        if (string.IsNullOrEmpty(fullPath)) { return; }

        generate.ClassName = Path.GetFileNameWithoutExtension(fullPath);
        if (generate.IsValidName(generate.ClassName))
        {
            string fileName = generate.serializeCfg.tempPath + generate.scriptName;
            generate.SaveScript(fullPath, fileName); AssetDatabase.Refresh();
            AssetDatabase.ImportAsset(fullPath, ImportAssetOptions.ForceSynchronousImport);
            Close(); GUIUtility.ExitGUI();
        }
        else
        {
            EditorUtility.DisplayDialog("Invalid class name", "You have chosen a class name " +
               "that cannot be used as a valid identifier for a MonoBehavior", "CANCEL");
        }
    }
}